import tkinter as tk
from tkinter import scrolledtext, ttk
import threading
import queue
import os
import zipfile
import schedule
import time
import re
from datetime import datetime
import configparser
# 单次和循环两种模式，单次在执行后直接结束

class FileCompressor:
    def __init__(self, root):
        self.root = root
        self.root.title("数据库文件压缩工具 著作:MrCui")
        self.root.geometry("480x650")
        self.root.geometry("+%d+%d" % (root.winfo_screenwidth() // 2 - 290, root.winfo_screenheight() // 2 - 325))

        # 初始化样式
        self.style = ttk.Style(root)
        self.style.theme_use('default')

        # 日志配置
        self.log_dir = r"C:\FilesLogs"
        self.log_file = os.path.join(self.log_dir, "app.log")
        self.config_file = os.path.join(self.log_dir, "config.ini")
        self.log_queue = queue.Queue()
        self.current_row = 0

        # 任务控制
        self.scheduler_running = False
        self.schedule_thread = None

        # 初始化日志系统
        self.init_log_system()
        self.create_widgets()
        # 加载配置，移到 create_widgets 之后
        self.load_config()
        self.root.after(100, self.process_log_queue)

    def init_log_system(self):
        """初始化日志系统"""
        try:
            # 创建日志目录（如果不存在）
            os.makedirs(self.log_dir, exist_ok=True)

            # 创建/清空日志文件（如果不存在）
            if not os.path.exists(self.log_file):
                with open(self.log_file, 'w') as f:
                    f.write("")
                self.insert_log(f"已创建日志文件：{self.log_file}")

            self.insert_log("========== 应用程序启动 著作:MrCui ==========")
        except Exception as e:
            self.log_queue.put(f"日志系统初始化失败：{str(e)}")

    def write_to_logfile(self, message):
        """写入日志文件"""
        try:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            with open(self.log_file, 'a', encoding='utf-8') as f:
                f.write(f"[{timestamp}] {message}\n")
        except Exception as e:
            self.log_queue.put(f"写入日志文件失败：{str(e)}")

    def create_widgets(self):
        """创建界面组件"""
        # 标题
        title_label = tk.Label(self.root,
                               text="数据库文件压缩工具",
                               font=("楷体", 12, "bold"),
                               fg="red")
        title_label.pack(side=tk.TOP, fill=tk.X, pady=(0, 0))

        font_style = ("楷体", 12)
        main_frame = tk.Frame(self.root, padx=20, pady=20)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 日志路径显示
        log_path_frame = tk.LabelFrame(main_frame,
                                       text="日志存储路径",
                                       font=font_style,
                                       padx=10,
                                       pady=10)
        log_path_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        self.log_path_var = tk.StringVar(value=self.log_file)
        tk.Label(log_path_frame, text="日志文件:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        log_entry = tk.Entry(log_path_frame,
                             textvariable=self.log_path_var,
                             width=50,
                             font=font_style,
                             state='readonly')
        log_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 文件路径输入区
        input_frame = tk.LabelFrame(main_frame,
                                    text="需要压缩文件的信息",
                                    font=font_style,
                                    padx=10,
                                    pady=10)
        input_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(input_frame, text="文件路径:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.source_path_entry = tk.Entry(input_frame, width=40, font=font_style)
        self.source_path_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        tk.Label(input_frame, text="文件类型:", font=font_style).grid(row=1, column=0, sticky=tk.W, pady=5)
        file_types = [".bak", ".sql", ".db", ".mdf", ".ldf"]
        self.file_type_combobox = ttk.Combobox(input_frame, values=file_types, width=37, font=font_style)
        self.file_type_combobox.set(file_types[0])
        self.file_type_combobox.grid(row=1, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 输出路径设置
        output_frame = tk.LabelFrame(main_frame,
                                     text="压缩后文件的存放信息",
                                     font=font_style,
                                     padx=10,
                                     pady=10)
        output_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(output_frame, text="存放路径:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.destination_path_entry = tk.Entry(output_frame, width=40, font=font_style)
        self.destination_path_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 定时设置区
        timer_frame = tk.LabelFrame(main_frame,
                                    text="定时器设置",
                                    font=font_style,
                                    padx=10,
                                    pady=10)
        timer_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(timer_frame, text="执行频率:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        frequencies = ["单次", "循环"]
        self.frequency_combobox = ttk.Combobox(timer_frame, values=frequencies, width=37, font=font_style)
        self.frequency_combobox.set(frequencies[0])
        self.frequency_combobox.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        tk.Label(timer_frame, text="执行时间段:", font=font_style).grid(row=1, column=0, sticky=tk.W, pady=5)
        self.time_var = tk.StringVar(value="00:00")
        self.time_entry = tk.Entry(timer_frame, textvariable=self.time_var, width=10, font=font_style)
        self.time_entry.grid(row=1, column=1, padx=10, pady=5, sticky=tk.W)
        self.time_entry.bind("<FocusOut>", self.validate_time_format)

        # 操作选项区
        action_frame = tk.LabelFrame(main_frame,
                                     text="执行后的操作",
                                     font=font_style,
                                     padx=10,
                                     pady=10)
        action_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        tk.Label(action_frame, text="操作选择:", font=font_style).grid(row=0, column=0, sticky=tk.W, pady=5)
        actions = ["删除源文件", "不删除源文件"]
        self.action_combobox = ttk.Combobox(action_frame, values=actions, width=37, font=font_style)
        self.action_combobox.set(actions[1])
        self.action_combobox.grid(row=0, column=1, padx=10, pady=5, sticky=tk.E + tk.W)

        # 控制按钮区
        button_frame = tk.Frame(main_frame)
        button_frame.pack(side=tk.TOP, fill=tk.X, pady=(0, 10))

        self.start_button = tk.Button(button_frame,
                                      text="开始",
                                      command=self.start_compression,
                                      font=font_style,
                                      width=10)
        self.start_button.pack(side=tk.LEFT, padx=10)

        self.stop_button = tk.Button(button_frame,
                                     text="结束",
                                     command=self.stop_compression,
                                     font=font_style,
                                     width=10,
                                     state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=10)

        # 日志显示区
        log_frame = tk.LabelFrame(main_frame,
                                  text="操作日志",
                                  font=font_style,
                                  padx=10,
                                  pady=10)
        log_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        self.text_box = scrolledtext.ScrolledText(log_frame, width=80, height=20, font=font_style)
        self.text_box.pack(fill=tk.BOTH, expand=True)

        self.row_label = tk.Label(main_frame, text="当前执行行数: 0", font=font_style)
        self.row_label.pack(side=tk.BOTTOM, pady=5)

    def validate_time_format(self, event):
        """验证时间格式"""
        time_str = self.time_var.get()
        pattern = r"^(?:[01]\d|2[0-3]):[0-5]\d$"
        if not re.match(pattern, time_str):
            self.insert_log("时间格式不正确，请输入 HH:MM 格式的时间", error=True)
            self.time_var.set("00:00")

    def toggle_controls(self, state):
        """切换控件状态"""
        controls = [
            self.source_path_entry,
            self.file_type_combobox,
            self.destination_path_entry,
            self.frequency_combobox,
            self.time_entry,
            self.action_combobox,
            self.start_button
        ]

        for control in controls:
            if isinstance(control, ttk.Combobox):
                control["state"] = "readonly" if state == tk.NORMAL else "disabled"
            else:
                control["state"] = state

        self.stop_button["state"] = tk.NORMAL if state == tk.DISABLED else tk.DISABLED

    def insert_log(self, message, error=False):
        """记录日志"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_message = f"[{timestamp}] {message}"

        # 写入队列和文件
        self.log_queue.put(log_message)
        self.write_to_logfile(log_message)

    def process_log_queue(self):
        """处理日志队列"""
        try:
            while True:
                message = self.log_queue.get_nowait()
                self.text_box.insert(tk.END, f"{message}\n")
                self.text_box.see(tk.END)
                self.current_row += 1
                self.row_label.config(text=f"当前执行行数: {self.current_row}")
        except queue.Empty:
            pass
        self.root.after(100, self.process_log_queue)

    def compress_files(self):
        """执行压缩操作"""
        try:
            source_path = self.source_path_entry.get()
            file_type = self.file_type_combobox.get()
            destination_path = self.destination_path_entry.get()
            action = self.action_combobox.get()

            # 参数验证
            if not all([source_path, file_type, destination_path]):
                self.insert_log("错误：请填写所有必要字段", error=True)
                return

            if not os.path.exists(source_path):
                self.insert_log(f"错误：源路径不存在 - {source_path}", error=True)
                return

            # 创建目标目录
            if not os.path.exists(destination_path):
                try:
                    os.makedirs(destination_path)
                    self.insert_log(f"已创建目标目录：{destination_path}")
                except Exception as e:
                    self.insert_log(f"创建目标目录失败：{str(e)}", error=True)
                    return

            # 执行压缩
            self.insert_log("开始文件扫描...")
            file_count = 0
            for root_dir, _, files in os.walk(source_path):
                for file in files:
                    if file.endswith(file_type):
                        file_path = os.path.join(root_dir, file)
                        zip_name = f"{os.path.splitext(file)[0]}.zip"
                        zip_path = os.path.join(destination_path, zip_name)

                        try:
                            with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                                zipf.write(file_path, arcname=file)
                            self.insert_log(f"成功压缩：{file} → {zip_name}")

                            if action == "删除源文件":
                                os.remove(file_path)
                                self.insert_log(f"已删除源文件：{file}")

                            file_count += 1
                        except Exception as e:
                            self.insert_log(f"压缩失败：{file} - {str(e)}", error=True)

            self.insert_log(f"操作完成，共处理 {file_count} 个文件")

            # 如果是单次执行，完成后停止任务
            if self.frequency_combobox.get() == "单次":
                self.stop_compression()

        except Exception as e:
            self.insert_log(f"发生未预期的错误：{str(e)}", error=True)

    def start_compression(self):
        """启动定时任务"""
        # 验证时间格式
        time_str = self.time_var.get()
        if not re.match(r"^(?:[01]\d|2[0-3]):[0-5]\d$", time_str):
            self.insert_log("错误：无效的时间格式（HH:MM）", error=True)
            return

        # 禁用控件
        self.toggle_controls(tk.DISABLED)
        self.scheduler_running = True

        # 设置定时任务
        frequency = self.frequency_combobox.get()
        try:
            if frequency == "单次":
                def single_run_task():
                    self.compress_files()
                    self.stop_compression()

                schedule.every().day.at(time_str).do(single_run_task)
            elif frequency == "循环":
                schedule.every().day.at(time_str).do(self.compress_files)
            else:
                raise ValueError("未知的执行频率")
        except Exception as e:
            self.insert_log(f"定时任务设置失败：{str(e)}", error=True)
            self.toggle_controls(tk.NORMAL)
            return

        # 保存配置
        self.save_config()

        # 启动调度线程
        def schedule_loop():
            while self.scheduler_running:
                schedule.run_pending()
                time.sleep(1)

        self.schedule_thread = threading.Thread(target=schedule_loop, daemon=True)
        self.schedule_thread.start()
        self.insert_log(f"定时任务已启动 - {frequency} {time_str}")

    def stop_compression(self):
        """停止定时任务"""
        self.scheduler_running = False
        schedule.clear()
        self.toggle_controls(tk.NORMAL)
        self.insert_log("定时任务已停止")

    def load_config(self):
        """加载配置文件"""
        if os.path.exists(self.config_file):
            config = configparser.ConfigParser()
            config.read(self.config_file)
            if 'Settings' in config:
                settings = config['Settings']
                self.source_path_entry.insert(0, settings.get('source_path', ''))
                self.file_type_combobox.set(settings.get('file_type', '.bak'))
                self.destination_path_entry.insert(0, settings.get('destination_path', ''))
                self.frequency_combobox.set(settings.get('frequency', '单次'))
                self.time_var.set(settings.get('time', '00:00'))
                self.action_combobox.set(settings.get('action', '不删除源文件'))

    def save_config(self):
        """保存配置文件"""
        config = configparser.ConfigParser()
        config['Settings'] = {
            'source_path': self.source_path_entry.get(),
            'file_type': self.file_type_combobox.get(),
            'destination_path': self.destination_path_entry.get(),
            'frequency': self.frequency_combobox.get(),
            'time': self.time_var.get(),
            'action': self.action_combobox.get()
        }
        with open(self.config_file, 'w') as configfile:
            config.write(configfile)


if __name__ == "__main__":
    root = tk.Tk()
    app = FileCompressor(root)
    root.mainloop()
